home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / patch / patch.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  21KB  |  905 lines

  1. char rcsid[] =
  2.     "$Header: patch.c,v 2.0.2.0 90/05/01 22:17:50 davison Locked $";
  3.  
  4. /* patch - a program to apply diffs to original files
  5.  *
  6.  * Copyright 1986, Larry Wall
  7.  *
  8.  * This program may be copied as long as you don't try to make any
  9.  * money off of it, or pretend that you wrote it.
  10.  *
  11.  * $Log:    patch.c,v $
  12.  * Revision 2.0.2.0  90/05/01  22:17:50  davison
  13.  * patch12u: unidiff support added
  14.  * 
  15.  * Revision 2.0.1.6  88/06/22  20:46:39  lwall
  16.  * patch12: rindex() wasn't declared
  17.  * 
  18.  * Revision 2.0.1.5  88/06/03  15:09:37  lwall
  19.  * patch10: exit code improved.
  20.  * patch10: better support for non-flexfilenames.
  21.  * 
  22.  * Revision 2.0.1.4  87/02/16  14:00:04  lwall
  23.  * Short replacement caused spurious "Out of sync" message.
  24.  * 
  25.  * Revision 2.0.1.3  87/01/30  22:45:50  lwall
  26.  * Improved diagnostic on sync error.
  27.  * Moved do_ed_script() to pch.c.
  28.  * 
  29.  * Revision 2.0.1.2  86/11/21  09:39:15  lwall
  30.  * Fuzz factor caused offset of installed lines.
  31.  * 
  32.  * Revision 2.0.1.1  86/10/29  13:10:22  lwall
  33.  * Backwards search could terminate prematurely.
  34.  * 
  35.  * Revision 2.0  86/09/17  15:37:32  lwall
  36.  * Baseline for netwide release.
  37.  * 
  38.  * Revision 1.5  86/08/01  20:53:24  lwall
  39.  * Changed some %d's to %ld's.
  40.  * Linted.
  41.  * 
  42.  * Revision 1.4  86/08/01  19:17:29  lwall
  43.  * Fixes for machines that can't vararg.
  44.  * Added fuzz factor.
  45.  * Generalized -p.
  46.  * General cleanup.
  47.  * 
  48.  * 85/08/15 van%ucbmonet@berkeley
  49.  * Changes for 4.3bsd diff -c.
  50.  *
  51.  * Revision 1.3  85/03/26  15:07:43  lwall
  52.  * Frozen.
  53.  * 
  54.  * Revision 1.2.1.9  85/03/12  17:03:35  lwall
  55.  * Changed pfp->_file to fileno(pfp).
  56.  * 
  57.  * Revision 1.2.1.8  85/03/12  16:30:43  lwall
  58.  * Check i_ptr and i_womp to make sure they aren't null before freeing.
  59.  * Also allow ed output to be suppressed.
  60.  * 
  61.  * Revision 1.2.1.7  85/03/12  15:56:13  lwall
  62.  * Added -p option from jromine@uci-750a.
  63.  * 
  64.  * Revision 1.2.1.6  85/03/12  12:12:51  lwall
  65.  * Now checks for normalness of file to patch.
  66.  * 
  67.  * Revision 1.2.1.5  85/03/12  11:52:12  lwall
  68.  * Added -D (#ifdef) option from joe@fluke.
  69.  * 
  70.  * Revision 1.2.1.4  84/12/06  11:14:15  lwall
  71.  * Made smarter about SCCS subdirectories.
  72.  * 
  73.  * Revision 1.2.1.3  84/12/05  11:18:43  lwall
  74.  * Added -l switch to do loose string comparison.
  75.  * 
  76.  * Revision 1.2.1.2  84/12/04  09:47:13  lwall
  77.  * Failed hunk count not reset on multiple patch file.
  78.  * 
  79.  * Revision 1.2.1.1  84/12/04  09:42:37  lwall
  80.  * Branch for sdcrdcf changes.
  81.  * 
  82.  * Revision 1.2  84/11/29  13:29:51  lwall
  83.  * Linted.  Identifiers uniqified.  Fixed i_ptr malloc() bug.  Fixed
  84.  * multiple calls to mktemp().  Will now work on machines that can only
  85.  * read 32767 chars.  Added -R option for diffs with new and old swapped.
  86.  * Various cosmetic changes.
  87.  * 
  88.  * Revision 1.1  84/11/09  17:03:58  lwall
  89.  * Initial revision
  90.  * 
  91.  */
  92.  
  93. #include "INTERN.h"
  94. #include "common.h"
  95. #include "EXTERN.h"
  96. #include "version.h"
  97. #include "util.h"
  98. #include "pch.h"
  99. #include "inp.h"
  100. #include "backupfile.h"
  101.  
  102. /* procedures */
  103.  
  104. void reinitialize_almost_everything();
  105. void get_some_switches();
  106. LINENUM locate_hunk();
  107. void abort_hunk();
  108. void apply_hunk();
  109. void init_output();
  110. void init_reject();
  111. void copy_till();
  112. void spew_output();
  113. void dump_line();
  114. bool patch_match();
  115. bool similar();
  116. void re_input();
  117. void my_exit();
  118.  
  119. /* TRUE if -E was specified on command line.  */
  120. static int remove_empty_files = FALSE;
  121.  
  122. /* TRUE if -R was specified on command line.  */
  123. static int reverse_flag_specified = FALSE;
  124.  
  125. /* Apply a set of diffs as appropriate. */
  126.  
  127. main(argc,argv)
  128. int argc;
  129. char **argv;
  130. {
  131.     LINENUM where;
  132.     LINENUM newwhere;
  133.     LINENUM fuzz;
  134.     LINENUM mymaxfuzz;
  135.     int hunk = 0;
  136.     int failed = 0;
  137.     int failtotal = 0;
  138.     int i;
  139.  
  140.     setbuf(stderr, serrbuf);
  141.     for (i = 0; i<MAXFILEC; i++)
  142.     filearg[i] = Nullch;
  143.  
  144.     /* Cons up the names of the temporary files.  */
  145.     {
  146.       /* Directory for temporary files.  */
  147.       char *tmpdir;
  148.       int tmpname_len;
  149.  
  150.       tmpdir = getenv ("TMPDIR");
  151.       if (tmpdir == NULL) {
  152.     tmpdir = "/tmp";
  153.       }
  154.       tmpname_len = strlen (tmpdir) + 20;
  155.  
  156.       TMPOUTNAME = (char *) malloc (tmpname_len);
  157.       strcpy (TMPOUTNAME, tmpdir);
  158.       strcat (TMPOUTNAME, "/patchoXXXXXX");
  159.       Mktemp(TMPOUTNAME);
  160.  
  161.       TMPINNAME = (char *) malloc (tmpname_len);
  162.       strcpy (TMPINNAME, tmpdir);
  163.       strcat (TMPINNAME, "/patchiXXXXXX");
  164.       Mktemp(TMPINNAME);
  165.  
  166.       TMPREJNAME = (char *) malloc (tmpname_len);
  167.       strcpy (TMPREJNAME, tmpdir);
  168.       strcat (TMPREJNAME, "/patchrXXXXXX");
  169.       Mktemp(TMPREJNAME);
  170.  
  171.       TMPPATNAME = (char *) malloc (tmpname_len);
  172.       strcpy (TMPPATNAME, tmpdir);
  173.       strcat (TMPPATNAME, "/patchpXXXXXX");
  174.       Mktemp(TMPPATNAME);
  175.     }
  176.  
  177.     /* parse switches */
  178.     Argc = argc;
  179.     Argv = argv;
  180.     {
  181.       char *v;
  182.  
  183.       v = getenv ("SIMPLE_BACKUP_SUFFIX");
  184.       if (v)
  185.     simple_backup_suffix = v;
  186.       else
  187.     simple_backup_suffix = ORIGEXT;
  188. #ifndef NODIR
  189.       v = getenv ("VERSION_CONTROL");
  190.       backup_type = get_version (v); /* OK to pass NULL. */
  191. #endif
  192.     }
  193.     get_some_switches();
  194.     
  195.     /* make sure we clean up /tmp in case of disaster */
  196.     set_signals(0);
  197.  
  198.     for (
  199.     open_patch_file(filearg[1]);
  200.     there_is_another_patch();
  201.     reinitialize_almost_everything()
  202.     ) {                    /* for each patch in patch file */
  203.  
  204.     if (outname == Nullch)
  205.         outname = savestr(filearg[0]);
  206.     
  207.     /* initialize the patched file */
  208.     if (!skip_rest_of_patch)
  209.         init_output(TMPOUTNAME);
  210.     
  211.     /* for ed script just up and do it and exit */
  212.     if (diff_type == ED_DIFF) {
  213.         do_ed_script();
  214.         continue;
  215.     }
  216.     
  217.     /* initialize reject file */
  218.     init_reject(TMPREJNAME);
  219.     
  220.     /* find out where all the lines are */
  221.     if (!skip_rest_of_patch)
  222.         scan_input(filearg[0]);
  223.     
  224.     /* from here on, open no standard i/o files, because malloc */
  225.     /* might misfire and we can't catch it easily */
  226.     
  227.     /* apply each hunk of patch */
  228.     hunk = 0;
  229.     failed = 0;
  230.     out_of_mem = FALSE;
  231.     while (another_hunk()) {
  232.         hunk++;
  233.         fuzz = Nulline;
  234.         mymaxfuzz = pch_context();
  235.         if (maxfuzz < mymaxfuzz)
  236.         mymaxfuzz = maxfuzz;
  237.         if (!skip_rest_of_patch) {
  238.         do {
  239.             where = locate_hunk(fuzz);
  240.             if (hunk == 1 && where == Nulline && !force) {
  241.                         /* dwim for reversed patch? */
  242.             if (!pch_swap()) {
  243.                 if (fuzz == Nulline)
  244.                 say1(
  245. "Not enough memory to try swapped hunk!  Assuming unswapped.\n");
  246.                 continue;
  247.             }
  248.             reverse = !reverse;
  249.             where = locate_hunk(fuzz);  /* try again */
  250.             if (where == Nulline) {        /* didn't find it swapped */
  251.                 if (!pch_swap())         /* put it back to normal */
  252.                 fatal1("Lost hunk on alloc error!\n");
  253.                 reverse = !reverse;
  254.             }
  255.             else if (noreverse) {
  256.                 if (!pch_swap())         /* put it back to normal */
  257.                 fatal1("Lost hunk on alloc error!\n");
  258.                 reverse = !reverse;
  259.                 say1(
  260. "Ignoring previously applied (or reversed) patch.\n");
  261.                 skip_rest_of_patch = TRUE;
  262.             }
  263.             else {
  264.                 ask3(
  265. "%seversed (or previously applied) patch detected!  %s -R? [y] ",
  266.                 reverse ? "R" : "Unr",
  267.                 reverse ? "Assume" : "Ignore");
  268.                 if (*buf == 'n') {
  269.                 ask1("Apply anyway? [n] ");
  270.                 if (*buf != 'y')
  271.                     skip_rest_of_patch = TRUE;
  272.                 where = Nulline;
  273.                 reverse = !reverse;
  274.                 if (!pch_swap())  /* put it back to normal */
  275.                     fatal1("Lost hunk on alloc error!\n");
  276.                 }
  277.             }
  278.             }
  279.         } while (!skip_rest_of_patch && where == Nulline &&
  280.             ++fuzz <= mymaxfuzz);
  281.  
  282.         if (skip_rest_of_patch) {        /* just got decided */
  283.             Fclose(ofp);
  284.             ofp = Nullfp;
  285.         }
  286.         }
  287.  
  288.         newwhere = pch_newfirst() + last_offset;
  289.         if (skip_rest_of_patch) {
  290.         abort_hunk();
  291.         failed++;
  292.         if (verbose)
  293.             say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
  294.         }
  295.         else if (where == Nulline) {
  296.         abort_hunk();
  297.         failed++;
  298.         if (verbose)
  299.             say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
  300.         }
  301.         else {
  302.         apply_hunk(where);
  303.         if (verbose) {
  304.             say3("Hunk #%d succeeded at %ld", hunk, newwhere);
  305.             if (fuzz)
  306.             say2(" with fuzz %ld", fuzz);
  307.             if (last_offset)
  308.             say3(" (offset %ld line%s)",
  309.                 last_offset, last_offset==1L?"":"s");
  310.             say1(".\n");
  311.         }
  312.         }
  313.     }
  314.  
  315.     if (out_of_mem && using_plan_a) {
  316.         Argc = Argc_last;
  317.         Argv = Argv_last;
  318.         say1("\n\nRan out of memory using Plan A--trying again...\n\n");
  319.         continue;
  320.     }
  321.     
  322.     assert(hunk);
  323.     
  324.     /* finish spewing out the new file */
  325.     if (!skip_rest_of_patch)
  326.         spew_output();
  327.     
  328.     /* and put the output where desired */
  329.     ignore_signals();
  330.     if (!skip_rest_of_patch) {
  331.         struct stat statbuf;
  332.         char *realout = outname;
  333.  
  334.         if (move_file(TMPOUTNAME, outname) < 0) {
  335.         toutkeep = TRUE;
  336.         realout = TMPOUTNAME;
  337.         chmod(TMPOUTNAME, filemode);
  338.         }
  339.         else
  340.         chmod(outname, filemode);
  341.  
  342.         if (remove_empty_files && stat(realout, &statbuf) == 0
  343.         && statbuf.st_size == 0) {
  344.         if (verbose)
  345.             say2("Removing %s (empty after patching).\n", realout);
  346.             while (unlink(realout) >= 0) ; /* while is for Eunice.  */
  347.         }
  348.     }
  349.     Fclose(rejfp);
  350.     rejfp = Nullfp;
  351.     if (failed) {
  352.         failtotal += failed;
  353.         if (!*rejname) {
  354.         Strcpy(rejname, outname);
  355. #ifndef FLEXFILENAMES
  356.         {
  357.             char *rindex();
  358.             char *s = rindex(rejname,'/');
  359.  
  360.             if (!s)
  361.             s = rejname;
  362.             if (strlen(s) > 13)
  363.             if (s[12] == '.')    /* try to preserve difference */
  364.                 s[12] = s[13];    /* between .h, .c, .y, etc. */
  365.             s[13] = '\0';
  366.         }
  367. #endif
  368.         Strcat(rejname, REJEXT);
  369.         }
  370.         if (skip_rest_of_patch) {
  371.         say4("%d out of %d hunks ignored--saving rejects to %s\n",
  372.             failed, hunk, rejname);
  373.         }
  374.         else {
  375.         say4("%d out of %d hunks failed--saving rejects to %s\n",
  376.             failed, hunk, rejname);
  377.         }
  378.         if (move_file(TMPREJNAME, rejname) < 0)
  379.         trejkeep = TRUE;
  380.     }
  381.     set_signals(1);
  382.     }
  383.     my_exit(failtotal);
  384. }
  385.  
  386. /* Prepare to find the next patch to do in the patch file. */
  387.  
  388. void
  389. reinitialize_almost_everything()
  390. {
  391.     re_patch();
  392.     re_input();
  393.  
  394.     input_lines = 0;
  395.     last_frozen_line = 0;
  396.  
  397.     filec = 0;
  398.     if (filearg[0] != Nullch && !out_of_mem) {
  399.     free(filearg[0]);
  400.     filearg[0] = Nullch;
  401.     }
  402.  
  403.     if (outname != Nullch) {
  404.     free(outname);
  405.     outname = Nullch;
  406.     }
  407.  
  408.     last_offset = 0;
  409.  
  410.     diff_type = 0;
  411.  
  412.     if (revision != Nullch) {
  413.     free(revision);
  414.     revision = Nullch;
  415.     }
  416.  
  417.     reverse = reverse_flag_specified;
  418.     skip_rest_of_patch = FALSE;
  419.  
  420.     get_some_switches();
  421.  
  422.     if (filec >= 2)
  423.     fatal1("You may not change to a different patch file.\n");
  424. }
  425.  
  426. static char *
  427. nextarg()
  428. {
  429.     if (!--Argc)
  430.     fatal2("patch: missing argument after `%s'\n", *Argv);
  431.     return *++Argv;
  432. }
  433.  
  434. /* Process switches and filenames up to next '+' or end of list. */
  435.  
  436. void
  437. get_some_switches()
  438. {
  439.     Reg1 char *s;
  440.  
  441.     rejname[0] = '\0';
  442.     Argc_last = Argc;
  443.     Argv_last = Argv;
  444.     if (!Argc)
  445.     return;
  446.     for (Argc--,Argv++; Argc; Argc--,Argv++) {
  447.     s = Argv[0];
  448.     if (strEQ(s, "+")) {
  449.         return;            /* + will be skipped by for loop */
  450.     }
  451.     if (*s != '-' || !s[1]) {
  452.         if (filec == MAXFILEC)
  453.         fatal1("patch: Too many file arguments.\n");
  454.         filearg[filec++] = savestr(s);
  455.     }
  456.     else {
  457.         switch (*++s) {
  458.         case 'b':
  459.         simple_backup_suffix = savestr(nextarg());
  460.         break;
  461.         case 'B':
  462.         origprae = savestr(nextarg());
  463.         break;
  464.         case 'c':
  465.         diff_type = CONTEXT_DIFF;
  466.         break;
  467.         case 'd':
  468.         if (!*++s)
  469.             s = nextarg();
  470.         if (chdir(s) < 0)
  471.             fatal2("Can't cd to %s.\n", s);
  472.         break;
  473.         case 'D':
  474.             do_defines = TRUE;
  475.         if (!*++s)
  476.             s = nextarg();
  477.         if (!isalpha(*s) && '_' != *s)
  478.             fatal1("Argument to -D not an identifier.\n");
  479.         Sprintf(if_defined, "#ifdef %s\n", s);
  480.         Sprintf(not_defined, "#ifndef %s\n", s);
  481.         Sprintf(end_defined, "#endif /* %s */\n", s);
  482.         break;
  483.         case 'e':
  484.         diff_type = ED_DIFF;
  485.         break;
  486.         case 'E':
  487.         remove_empty_files = TRUE;
  488.         break;
  489.         case 'f':
  490.         force = TRUE;
  491.         break;
  492.         case 'F':
  493.         if (*++s == '=')
  494.             s++;
  495.         maxfuzz = atoi(s);
  496.         break;
  497.         case 'l':
  498.         canonicalize = TRUE;
  499.         break;
  500.         case 'n':
  501.         diff_type = NORMAL_DIFF;
  502.         break;
  503.         case 'N':
  504.         noreverse = TRUE;
  505.         break;
  506.         case 'o':
  507.         outname = savestr(nextarg());
  508.         break;
  509.         case 'p':
  510.         if (*++s == '=')
  511.             s++;
  512.         strippath = atoi(s);
  513.         break;
  514.         case 'r':
  515.         Strcpy(rejname, nextarg());
  516.         break;
  517.         case 'R':
  518.         reverse = TRUE;
  519.         reverse_flag_specified = TRUE;
  520.         break;
  521.         case 's':
  522.         verbose = FALSE;
  523.         break;
  524.         case 'S':
  525.         skip_rest_of_patch = TRUE;
  526.         break;
  527.         case 'u':
  528.         diff_type = UNI_DIFF;
  529.         break;
  530.         case 'v':
  531.         version();
  532.         break;
  533.         case 'V':
  534. #ifndef NODIR
  535.         backup_type = get_version (nextarg ());
  536. #endif
  537.         break;
  538. #ifdef DEBUGGING
  539.         case 'x':
  540.         debug = atoi(s+1);
  541.         break;
  542. #endif
  543.         default:
  544.         fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]);
  545.         fprintf(stderr, "\
  546. Usage: patch [-ceEflnNRsSuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
  547.        [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\
  548.        [-r rej-name] [-V {numbered,existing,simple}] [origfile] [patchfile]\n\
  549.        [[+] [options] [origfile]...]\n");
  550.         my_exit(1);
  551.         }
  552.     }
  553.     }
  554. }
  555.  
  556. /* Attempt to find the right place to apply this hunk of patch. */
  557.  
  558. LINENUM
  559. locate_hunk(fuzz)
  560. LINENUM fuzz;
  561. {
  562.     Reg1 LINENUM first_guess = pch_first() + last_offset;
  563.     Reg2 LINENUM offset;
  564.     LINENUM pat_lines = pch_ptrn_lines();
  565.     Reg3 LINENUM max_pos_offset = input_lines - first_guess
  566.                 - pat_lines + 1; 
  567.     Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
  568.                 + pch_context();
  569.  
  570.     if (!pat_lines)            /* null range matches always */
  571.     return first_guess;
  572.     if (max_neg_offset >= first_guess)    /* do not try lines < 0 */
  573.     max_neg_offset = first_guess - 1;
  574.     if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
  575.     return first_guess;
  576.     for (offset = 1; ; offset++) {
  577.     Reg5 bool check_after = (offset <= max_pos_offset);
  578.     Reg6 bool check_before = (offset <= max_neg_offset);
  579.  
  580.     if (check_after && patch_match(first_guess, offset, fuzz)) {
  581. #ifdef DEBUGGING
  582.         if (debug & 1)
  583.         say3("Offset changing from %ld to %ld\n", last_offset, offset);
  584. #endif
  585.         last_offset = offset;
  586.         return first_guess+offset;
  587.     }
  588.     else if (check_before && patch_match(first_guess, -offset, fuzz)) {
  589. #ifdef DEBUGGING
  590.         if (debug & 1)
  591.         say3("Offset changing from %ld to %ld\n", last_offset, -offset);
  592. #endif
  593.         last_offset = -offset;
  594.         return first_guess-offset;
  595.     }
  596.     else if (!check_before && !check_after)
  597.         return Nulline;
  598.     }
  599. }
  600.  
  601. /* We did not find the pattern, dump out the hunk so they can handle it. */
  602.  
  603. void
  604. abort_hunk()
  605. {
  606.     Reg1 LINENUM i;
  607.     Reg2 LINENUM pat_end = pch_end();
  608.     /* add in last_offset to guess the same as the previous successful hunk */
  609.     LINENUM oldfirst = pch_first() + last_offset;
  610.     LINENUM newfirst = pch_newfirst() + last_offset;
  611.     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
  612.     LINENUM newlast = newfirst + pch_repl_lines() - 1;
  613.     char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
  614.     char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
  615.  
  616.     fprintf(rejfp, "***************\n");
  617.     for (i=0; i<=pat_end; i++) {
  618.     switch (pch_char(i)) {
  619.     case '*':
  620.         if (oldlast < oldfirst)
  621.         fprintf(rejfp, "*** 0%s\n", stars);
  622.         else if (oldlast == oldfirst)
  623.         fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
  624.         else
  625.         fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
  626.         break;
  627.     case '=':
  628.         if (newlast < newfirst)
  629.         fprintf(rejfp, "--- 0%s\n", minuses);
  630.         else if (newlast == newfirst)
  631.         fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
  632.         else
  633.         fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
  634.         break;
  635.     case '\n':
  636.         fprintf(rejfp, "%s", pfetch(i));
  637.         break;
  638.     case ' ': case '-': case '+': case '!':
  639.         fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
  640.         break;
  641.     default:
  642.         say1("Fatal internal error in abort_hunk().\n"); 
  643.         abort();
  644.     }
  645.     }
  646. }
  647.  
  648. /* We found where to apply it (we hope), so do it. */
  649.  
  650. void
  651. apply_hunk(where)
  652. LINENUM where;
  653. {
  654.     Reg1 LINENUM old = 1;
  655.     Reg2 LINENUM lastline = pch_ptrn_lines();
  656.     Reg3 LINENUM new = lastline+1;
  657. #define OUTSIDE 0
  658. #define IN_IFNDEF 1
  659. #define IN_IFDEF 2
  660. #define IN_ELSE 3
  661.     Reg4 int def_state = OUTSIDE;
  662.     Reg5 bool R_do_defines = do_defines;
  663.     Reg6 LINENUM pat_end = pch_end();
  664.  
  665.     where--;
  666.     while (pch_char(new) == '=' || pch_char(new) == '\n')
  667.     new++;
  668.     
  669.     while (old <= lastline) {
  670.     if (pch_char(old) == '-') {
  671.         copy_till(where + old - 1);
  672.         if (R_do_defines) {
  673.         if (def_state == OUTSIDE) {
  674.             fputs(not_defined, ofp);
  675.             def_state = IN_IFNDEF;
  676.         }
  677.         else if (def_state == IN_IFDEF) {
  678.             fputs(else_defined, ofp);
  679.             def_state = IN_ELSE;
  680.         }
  681.         fputs(pfetch(old), ofp);
  682.         }
  683.         last_frozen_line++;
  684.         old++;
  685.     }
  686.     else if (new > pat_end) {
  687.         break;
  688.     }
  689.     else if (pch_char(new) == '+') {
  690.         copy_till(where + old - 1);
  691.         if (R_do_defines) {
  692.         if (def_state == IN_IFNDEF) {
  693.             fputs(else_defined, ofp);
  694.             def_state = IN_ELSE;
  695.         }
  696.         else if (def_state == OUTSIDE) {
  697.             fputs(if_defined, ofp);
  698.             def_state = IN_IFDEF;
  699.         }
  700.         }
  701.         fputs(pfetch(new), ofp);
  702.         new++;
  703.     }
  704.     else if (pch_char(new) != pch_char(old)) {
  705.         say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
  706.         pch_hunk_beg() + old,
  707.         pch_hunk_beg() + new);
  708. #ifdef DEBUGGING
  709.         say3("oldchar = '%c', newchar = '%c'\n",
  710.         pch_char(old), pch_char(new));
  711. #endif
  712.         my_exit(1);
  713.     }
  714.     else if (pch_char(new) == '!') {
  715.         copy_till(where + old - 1);
  716.         if (R_do_defines) {
  717.            fputs(not_defined, ofp);
  718.            def_state = IN_IFNDEF;
  719.         }
  720.         while (pch_char(old) == '!') {
  721.         if (R_do_defines) {
  722.             fputs(pfetch(old), ofp);
  723.         }
  724.         last_frozen_line++;
  725.         old++;
  726.         }
  727.         if (R_do_defines) {
  728.         fputs(else_defined, ofp);
  729.         def_state = IN_ELSE;
  730.         }
  731.         while (pch_char(new) == '!') {
  732.         fputs(pfetch(new), ofp);
  733.         new++;
  734.         }
  735.     }
  736.     else {
  737.         assert(pch_char(new) == ' ');
  738.         old++;
  739.         new++;
  740.         if (R_do_defines && def_state != OUTSIDE) {
  741.         fputs(end_defined, ofp);
  742.         def_state = OUTSIDE;
  743.         }
  744.     }
  745.     }
  746.     if (new <= pat_end && pch_char(new) == '+') {
  747.     copy_till(where + old - 1);
  748.     if (R_do_defines) {
  749.         if (def_state == OUTSIDE) {
  750.             fputs(if_defined, ofp);
  751.         def_state = IN_IFDEF;
  752.         }
  753.         else if (def_state == IN_IFNDEF) {
  754.         fputs(else_defined, ofp);
  755.         def_state = IN_ELSE;
  756.         }
  757.     }
  758.     while (new <= pat_end && pch_char(new) == '+') {
  759.         fputs(pfetch(new), ofp);
  760.         new++;
  761.     }
  762.     }
  763.     if (R_do_defines && def_state != OUTSIDE) {
  764.     fputs(end_defined, ofp);
  765.     }
  766. }
  767.  
  768. /* Open the new file. */
  769.  
  770. void
  771. init_output(name)
  772. char *name;
  773. {
  774.     ofp = fopen(name, "w");
  775.     if (ofp == Nullfp)
  776.     fatal2("patch: can't create %s.\n", name);
  777. }
  778.  
  779. /* Open a file to put hunks we can't locate. */
  780.  
  781. void
  782. init_reject(name)
  783. char *name;
  784. {
  785.     rejfp = fopen(name, "w");
  786.     if (rejfp == Nullfp)
  787.     fatal2("patch: can't create %s.\n", name);
  788. }
  789.  
  790. /* Copy input file to output, up to wherever hunk is to be applied. */
  791.  
  792. void
  793. copy_till(lastline)
  794. Reg1 LINENUM lastline;
  795. {
  796.     Reg2 LINENUM R_last_frozen_line = last_frozen_line;
  797.  
  798.     if (R_last_frozen_line > lastline)
  799.     fatal1("patch: misordered hunks! output will be garbled.\n");
  800.     while (R_last_frozen_line < lastline) {
  801.     dump_line(++R_last_frozen_line);
  802.     }
  803.     last_frozen_line = R_last_frozen_line;
  804. }
  805.  
  806. /* Finish copying the input file to the output file. */
  807.  
  808. void
  809. spew_output()
  810. {
  811. #ifdef DEBUGGING
  812.     if (debug & 256)
  813.     say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
  814. #endif
  815.     if (input_lines)
  816.     copy_till(input_lines);        /* dump remainder of file */
  817.     Fclose(ofp);
  818.     ofp = Nullfp;
  819. }
  820.  
  821. /* Copy one line from input to output. */
  822.  
  823. void
  824. dump_line(line)
  825. LINENUM line;
  826. {
  827.     Reg1 char *s;
  828.     Reg2 char R_newline = '\n';
  829.  
  830.     /* Note: string is not null terminated. */
  831.     for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
  832. }
  833.  
  834. /* Does the patch pattern match at line base+offset? */
  835.  
  836. bool
  837. patch_match(base, offset, fuzz)
  838. LINENUM base;
  839. LINENUM offset;
  840. LINENUM fuzz;
  841. {
  842.     Reg1 LINENUM pline = 1 + fuzz;
  843.     Reg2 LINENUM iline;
  844.     Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
  845.  
  846.     for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
  847.     if (canonicalize) {
  848.         if (!similar(ifetch(iline, (offset >= 0)),
  849.              pfetch(pline),
  850.              pch_line_len(pline) ))
  851.         return FALSE;
  852.     }
  853.     else if (strnNE(ifetch(iline, (offset >= 0)),
  854.            pfetch(pline),
  855.            pch_line_len(pline) ))
  856.         return FALSE;
  857.     }
  858.     return TRUE;
  859. }
  860.  
  861. /* Do two lines match with canonicalized white space? */
  862.  
  863. bool
  864. similar(a,b,len)
  865. Reg1 char *a;
  866. Reg2 char *b;
  867. Reg3 int len;
  868. {
  869.     while (len) {
  870.     if (isspace(*b)) {        /* whitespace (or \n) to match? */
  871.         if (!isspace(*a))        /* no corresponding whitespace? */
  872.         return FALSE;
  873.         while (len && isspace(*b) && *b != '\n')
  874.         b++,len--;        /* skip pattern whitespace */
  875.         while (isspace(*a) && *a != '\n')
  876.         a++;            /* skip target whitespace */
  877.         if (*a == '\n' || *b == '\n')
  878.         return (*a == *b);    /* should end in sync */
  879.     }
  880.     else if (*a++ != *b++)        /* match non-whitespace chars */
  881.         return FALSE;
  882.     else
  883.         len--;            /* probably not necessary */
  884.     }
  885.     return TRUE;            /* actually, this is not reached */
  886.                     /* since there is always a \n */
  887. }
  888.  
  889. /* Exit with cleanup. */
  890.  
  891. void
  892. my_exit(status)
  893. int status;
  894. {
  895.     Unlink(TMPINNAME);
  896.     if (!toutkeep) {
  897.     Unlink(TMPOUTNAME);
  898.     }
  899.     if (!trejkeep) {
  900.     Unlink(TMPREJNAME);
  901.     }
  902.     Unlink(TMPPATNAME);
  903.     exit(status);
  904. }
  905.